<?php
// +------------------+--------------+------+-----+---------+----------------+
// | Field            | Type         | Null | Key | Default | Extra          |
// +------------------+--------------+------+-----+---------+----------------+
// | id               | int(11)      | NO   | PRI | NULL    | auto_increment |
// | name             | varchar(60)  | NO   |     |         |                |
// | password         | varchar(64)  | NO   |     |         |                |
// | nickname         | varchar(50)  | YES  |     |         |                |
// | email            | varchar(50)  | YES  |     |         |                |
// | url              | varchar(100) | YES  |     |         |                |
// | avatar           | varchar(200) | YES  |     |         |                |
// | gender           | varchar(10)  | YES  |     |         |                |
// | birthday         | date         | YES  |     | NULL    |                |
// | signature        | varchar(200) | YES  |     |         |                |
// | last_login_ip    | bigint(20)   | YES  |     | 0       |                |
// | last_login_time  | int(11)      | YES  |     | 0       |                |
// | activation_key   | varchar(60)  | YES  |     |         |                |
// | status           | tinyint(1)   | YES  |     | 1       |                |
// | active_score     | int(11)      | YES  |     | 0       |                |
// | user_type        | varchar(20)  | YES  |     |         |                |
// | active_coin      | int(11)      | YES  |     | 0       |                |
// | mobile           | varchar(20)  | YES  |     |         |                |
// | create_time      | int(11)      | YES  |     | 0       |                |
// | update_time      | int(11)      | YES  |     | 0       |                |
// | editor_id        | int(11)      | YES  |     | 0       |                |
// | manager_group_id | int(11)      | YES  |     | 0       |                |
// | is_locked        | tinyint(1)   | YES  |     | 0       |                |
// | total_score      | int(11)      | YES  |     | 0       |                |
// | total_coin       | int(11)      | YES  |     | 0       |                |
// | member_group_id  | int(11)      | YES  |     | 0       |                |
// | province         | int(11)      | YES  |     | 0       |                |
// | city             | int(11)      | YES  |     | 0       |                |
// | address          | varchar(255) | YES  |     |         |                |
// | description      | varchar(255) | YES  |     |         |                |
// +------------------+--------------+------+-----+---------+----------------+
namespace app\admin\model;
use think\facade\Config;
use app\admin\model\Base;
use app\admin\model\AuthGroup;
use app\admin\model\ScoreLog;
use app\admin\model\GroupAccess;
use app\admin\validate\User as ModelValidate;
use think\facade\Request;
class User extends Base{
  public function memberGroup(){
    return $this->belongsTo('auth_group', 'member_group_id');
  }

  public function managerGroup(){
    return $this->belongsTo('auth_group', 'manager_group_id');
  }

  /**
   * 添加新用户
   * @Author zhanghong
   * @Date   2016-12-22
   * @param  [string]     $user_type 用户类型
   * @param  [array]      $data      用户数据
   * @return [array]                     操作是否成功
   */
  public function saveItem($data){
    $res = ["status" => false, "id" => 0, "msg" => "添加用户失败"];

    $validate = new ModelValidate();
    // user_type是manager or member
    if($validate->scene($data["user_type"])->check($data)){
      $data["password"] = $this->md5Password($data["password"]);
      $this->data($data)->allowField(true)->save();
      $id = $this->id;

      if(isset($data["active_score"])){
        $inc_score = intval($data["active_score"]);
      }else{
        $inc_score = 0;
      }
      if(isset($data["active_coin"])){
        $inc_coin = intval($data["active_coin"]);
      }else{
        $inc_coin = 0;
      }
      // 记录用户积分
      $this->updateUserScore($id, $inc_score, $inc_coin, "创建用户", ["reset_score" => true, "reset_coin" => true]);

      // 更新管理员级
      // if($data["user_type"] == "manager" && isset($data["manager_group_id"])){
      //   $AccessModel = new GroupAccess();
      //   $access_res = $AccessModel->createOrUpdateUserAccess($id, $data["manager_group_id"], "manager");
      // }

      $res = ["status" => true, "id" => $id, "msg" => "添加用户成功"];
    }else{
      $res["msg"] = $validate->getError();
    }

    return $res;
  }

  /**
   * 更新用户基本信息
   * @Author zhanghong
   * @Date   2016-12-23
   * @param  string     $user_type 用户类型
   * @param  array      $data      用户信息
   * @param  integer    $id        用户ID
   * @return array                 更新结果
   */
  public function updateBaseInfo($data, $id = 0){
    $res = ["status" => false, "msg" => "更新用户信息成功", "user" => NULL];
    if(!empty($data["id"])){
      $id = intval($data["id"]);
    }

    if($id <= 0){
      $res["msg"] = "用户ID不正确";
      return $res;
    }

    if(empty($data["user_type"])){
      $res["msg"] = "用户类型不能为空";
      return $res;
    }else if(!in_array($data["user_type"], ["member", "manager"])){
      $res["msg"] = "用户类型不正确";
      return $res;
    }

    //如果在更新字段里不包含id，而是放在where条件里, 邮箱唯一验证会失败
    $allow_data = ["id" => $id];
    // 只能修改以下几个字段
    $all_fields = ["confirm_password", "password", "nickname", "url", "avatar", "gender", "birthday", "coin", "manager_group_id", "is_locked", "email", "name", "province", "city", "address", "description"];
    foreach ($data as $key => $value) {
      if(in_array($key, $all_fields)){
        $allow_data[$key] = $data[$key];
      }
    }

    $validate = new ModelValidate();
    // user_type是manager or member
    if($validate->scene($data["user_type"])->check($allow_data)){
      if(!empty($allow_data["password"])){
        $allow_data["password"] = $this->md5Password($allow_data["password"]);
        unset($allow_data["confirm_password"]);
      }

      $this->allowField(true)->isUpdate(true)->save($allow_data);

      $user = $this->where("id", $id)->find();
      if(isset($data["active_score"])){
        $inc_score = $data["active_score"] - $user["active_score"];
      }else{
        $inc_score = 0;
      }
      if(isset($data["active_coin"])){
        $inc_coin = $data["active_coin"] - $user["active_coin"];
      }else{
        $inc_coin = 0;
      }
      if($inc_score != 0 || $inc_coin != 0){
        // 更新会员等级信息
        $this->updateUserScore($id, $inc_score, $inc_coin, "更新用户积分");
      }

      // // 更新管理员级
      // if($data["user_type"] == "manager" && isset($data["manager_group_id"])){
      //   $AccessModel = new GroupAccess();
      //   $access_res = $AccessModel->createOrUpdateUserAccess($id, $data["manager_group_id"], "manager");
      // }

      $res = ["status" => true, "msg" => "更新用户信息成功"];
    }else{
      $res["msg"] = $validate->getError();
    }

    return $res;
  }

    /**
   * 重置用户密码
   * @Author zhanghong
   * @Date   2017-05-16
   * @param  [type]     $data        [description]
   * @param  boolean    $only_active [description]
   * @return [type]                  [description]
   */
  public function retrievePassword($data, $only_active = true){
    $res = ["status" => false, "msg" => "密码更新失败"];

    // data包含 mobile(手机号) password(新密码), confirm_password(确认密码)

    $mobile = $data["mobile"];
    $this->where("mobile", $mobile);
    if($only_active){
      $this->where("status", 1);
    }

    $user = $this->find();
    $data['id'] = $user['id'];
    if(empty($user)){
      $res = ["status" => false, "msg" => "没有此帐号"];
      return $res;
    }

    $validate = new ModelValidate();
    // 验证密码是否符合validate
    if($validate->scene($user["user_type"])->check($data)){
      $data["password"] = $this->md5Password($data["password"]);
      $this->allowField(true)->save($data, ["id" => $user["id"]]);
      $res = ["status" => true, "msg" => "重置密码成功"];
    }else{
      $res["msg"] = $validate->getError();
    }

    return $res;
  }

  /**
   * 重新加载用户登录信息
   * @Author zhanghong
   * @Date   2017-03-12
   * @param  integer    $id        用户ID
   * @param  string     $user_type 用户类型
   * @return array
   */
  public function reloadUserInfo($id, $user_type = NULL){
    $param = [];
    $param["id"] = $id;
    if(!empty($user_type)){
      $param["user_type"] = $user_type;
    }
    return $this->findLoginInfo($param);
  }

  /**
   * 更新用户Score/Coin
   * @Author zhanghong
   * @Date   2017-01-09
   * @param  integer    $id        用户ID
   * @param  integer    $inc_score 增加score
   * @param  integer    $inc_coin  增加coin
   * @param  string     $comment   备注信息
   * @param  array      $option    日志扩展信息
   * @return array
   */
  private function updateUserScore($id, $inc_score, $inc_coin, $comment, $option = []){
    $res = ["status" => false, "msg" => "更新用户积分失败", "data" => []];

    $user = $this->where("id", $id)->find();
    if(empty($user)){
      $res["msg"] = "用户不存存";
      return $res;
    }

    $data = [];
    $log_data = [];
    if(isset($option["reset_score"])){
      $data["total_score"] = $inc_score;
      $data["active_score"] = $inc_score;
    }else if($inc_score > 0){
      $data["total_score"] = $user["total_score"]+$inc_score;
      $data["active_score"] = $user["active_score"]+$inc_score;
    }else if($inc_score < 0){
      $data["total_score"] = $user["total_score"];
      $data["active_score"] = $user["active_score"]+$inc_score;
    }

    if(isset($option["reset_coin"])){
      $data["total_coin"] = $inc_coin;
      $data["active_coin"] = $inc_coin;
    }else if($inc_coin > 0){
      $data["total_coin"] = $user["total_coin"]+$inc_coin;
      $data["active_coin"] = $user["active_coin"]+$inc_coin;
    }else if($inc_coin < 0){
      $data["total_coin"] = $user["total_coin"];
      $data["active_coin"] = $user["active_coin"]+$inc_coin;
    }

    if(empty($data)){
      $res["msg"] = "积分更新数据为空";
      return $res;
    }

    $validate = new ModelValidate();
    if($validate->scene("score_coin")->check($data)){
      if(isset($data["total_score"])){
        $current_total_score = $data["total_score"];
      }else{
        $current_total_score = $user["total_score"];
      }
      // // 计算用户当前等级
      // $GroupModel = new AuthGroup();
      // $group_item = $GroupModel->memberGroupItem($current_total_score);
      // if(empty($group_item)){
      //   // 没有符合条件的注册会员组，不更新当前用户的group_id
      //   // $group_id = $user["member_group_id"];
      // }else{
      //   $group_id = $group_item["id"];
      //   $data["member_group_id"] = $group_id;
      // }
      $this->save($data, ['id' => $id]);

      // if(isset($data["member_group_id"])){
      //   // 创建或更新用户的GroupAccess记录
      //   $AccessModel = new GroupAccess();
      //   $access_res = $AccessModel->createOrUpdateUserAccess($id, $group_id, "member");
      // }

      $log_data = [
        'user_id' => $id,
        'inc_score' => $inc_score,
        'inc_coin' => $inc_coin,
        'comment' => $comment,
      ];
      // 记录option字段
      $log_data = array_merge($option, $log_data);
      $LogModel = new ScoreLog();
      $log_id = $LogModel->addLog($log_data);
      $res = ["status" => true, "msg" => "更新用户积分成功", "data" => ["log_id" => $log_id]];
    }else{
      $res["msg"] = $validate->getError();
    }
    return $res;
  }

  /**
   * 用户登录信息
   * @Author zhanghong
   * @Date   2017-01-01
   * @param  array      $param  搜索参数
   * @return array              User
   */
  private function findLoginInfo($param){
    $user = array();
    if(empty($param)){
      return $user;
    }

    $map_count = 0;
    $this->field(["id", "nickname", "email", "url", "avatar", "gender", "birthday", "last_login_ip", "last_login_time", "active_score", "active_coin", "mobile", "create_time", "update_time", "user_type"]);

    foreach ($param as $key => $value) {
      switch ($key) {
      case "password":
        $password = $this->md5Password($value);
        $this->where("password", $password);
        $map_count++;
        break;
      default:
        $this->where($key, $value);
        $map_count++;
        break;
      }
    }

    // 默认参数（status=true, is_locked=false）
    $this->where("status", true)->where("is_locked", 0);

    if($map_count > 0){
      $user = $this->find();
    }

    return $user;
  }

  /**
   * 后台管理员登录
   * @Author zhanghong
   * @Date   2016-12-22
   * @param  [string]     $mobile   登录名(手机号码)
   * @param  [string]     $password 登录密码
   * @return [array]                登录用户信息
   */
  public function managerLogin($mobile, $password){
    if(empty($mobile)){
      return NULL;
    }

    $param = ["mobile" => $mobile, "password" => $password, "user_type" => "manager"];
    $user = $this->findLoginInfo($param);

    if(!empty($user)){
      $request = Request::instance();
      $data = ["last_login_ip" => ip2long($request->ip()), "last_login_time" => time()];
      $user->save($data, ['id' => $user["id"]]);
    }
    return $user;
  }


  /**
   * 更新用户密码
   * @Author zhanghong
   * @Date   2016-12-22
   * @param  [integer]     $id           用户ID
   * @param  [array]       $data         密码信息
   * @param  boolean       $only_active  是否只查询有效用户
   * @return [array]                     操作是否成功
   */
  public function changePassword($id, $data, $only_active = true){
    $res = ["status" => false, "msg" => "密码更新失败"];

    // data包含 password(新密码), old_password(旧密码)，confirm_password(确认密码)
    $id = intval($id);

    $password = $this->md5Password($data["old_password"]);
    $this->where("id", $id);
    $this->where("password", $password);
    if($only_active){
      $this->where("status", 1);
    }

    $user = $this->find();
    if(empty($user)){
      $res["status"] = "旧密码错误";
      return $res;
    }

    $validate = new ModelValidate();
    // 验证密码是否符合validate
    if($validate->scene($user["user_type"])->check($data)){
      $data["password"] = $this->md5Password($data["password"]);
      $this->allowField(true)->save($data, ["id" => $user["id"]]);
      $res = ["status" => true, "msg" => "密码更新成功"];
    }else{
      $res["msg"] = $validate->getError();
    }

    return $res;
  }

  /**
   * 查询方法
   * @Author zhanghong
   * @Date   2016-12-22
   * @param  [string]     $user_type 用户类型
   * @param  [array]      $param     查询参数
   * @param  [integer]    $page      当前页码
   * @param  integer      $page_rows 每页显示的记录数
   * @return \think\paginator\Collection
   */
  public function memberPaginate($param, $list_rows = NULL){
    if(!is_array($param)){
      $param = [];
    }
    $param['user_type'] = 'member';
    $map = $this->mapSearchParam($param);

    // order string
    if(isset($param['sort'])){
      $order_type = $param['sort'];
    }else{
      $order_type = 'user_id';
    }
    $order_str = $this->orderStr($order_type);

    if(empty($list_rows)){
      $list_rows = Config::get('app.paginate.list_rows');
    }

    $config = ['query' => $param];
    // 分页参数名是page ，paginate方法内有获取到该值的方法
    $page = 1;
    if(isset($param['page'])){
      $page = intval($param['page']);
    }
    $config['page'] = $page;

    $select_fields = [];
    $user_fields = ["id", "avatar", "nickname", "mobile", "email", "last_login_ip", "last_login_time", "is_locked", "active_score", "total_score", "active_coin", "total_coin"];
    foreach ($user_fields as $idx => $key) {
      array_push($select_fields, "user.{$key}");
    }
    array_push($select_fields, "mb_group.title AS member_group_title");

    $paginate =  $this->alias('user')->field($select_fields)
                  ->leftJoin('auth_group mb_group', 'user.member_group_id=mb_group.id')
                  ->order($order_str)
                  ->paginate($list_rows, false, $config);
    return $paginate;
  }

  /**
   * 查询方法
   * @Author zhanghong
   * @Date   2016-12-22
   * @param  array        $param     查询参数
   * @param  integer      $page_rows 每页显示的记录数
   * @return \think\paginator\Collection
   */
  public function managerPaginate($param, $list_rows = NULL){
    if(!is_array($param)){
      $param = [];
    }
    $param['user_type'] = 'manager';
    $map = $this->mapSearchParam($param);

    // order string
    if(isset($param['sort'])){
      $order_type = $param['sort'];
    }else{
      $order_type = 'user_id';
    }
    $order_str = $this->orderStr($order_type);

    if(empty($list_rows)){
      $list_rows = Config::get('app.paginate.list_rows');
    }

    $config = ['query' => $param];
    // 分页参数名是page ，paginate方法内有获取到该值的方法
    $page = 1;
    if(isset($param['page'])){
      $page = intval($param['page']);
    }
    $config['page'] = $page;

    $select_fields = [];
    $user_fields = ["id", "avatar", "nickname", "mobile", "email", "last_login_ip", "last_login_time", "is_locked", "active_score", "total_score", "active_coin", "total_coin"];
    foreach ($user_fields as $idx => $key) {
      array_push($select_fields, "user.{$key}");
    }
    array_push($select_fields, "mng_group.title AS manager_group_title");

    $paginate =  $this->alias('user')->field($select_fields)
                  ->join('auth_group mng_group', 'user.manager_group_id=mng_group.id')
                  ->order($order_str)
                  ->paginate($list_rows, false, $config);
    return $paginate;
  }

  /**
   * 验证用户登录密码
   * @Author zhanghong
   * @Date   2017-01-01
   * @param  integer    $id       用户ID
   * @param  string     $password 登录密码
   * @return string               true/false
   */
  public function validPassword($id, $password){
    $count = $this->where("id", $id)->where("status", 1)
                  ->where("password", $this->md5Password($password))
                  ->count();
    if($count){
      return "true";
    }else{
      return "false";
    }
  }

  /**
   * 验证字段值是否唯一
   * @Author zhanghong
   * @Date   2017-01-07
   * @return [type]     [description]
   */
  public function isFieldValueUnique(){
    $is_valid = false;
    $request = Request::instance();
    $id = $request->post("id");
    $field_name = $request->post("field");
    if(empty($field_name)){
      return $is_valid;
    }
    $field_value = $request->post($field_name);
    if(empty($field_value)){
      return $is_valid;
    }
    $count = $this->where("id", "<>", intval($id))->where($field_name, $field_value)->count();
    if(!$count){
      $is_valid = true;
    }
    return $is_valid;
  }

  /**
   * 修改用户类型
   * @Author zhanghong
   * @Date   2017-04-23
   * @param  integer    $id        用户ID
   * @param  string     $user_type 修改后的用户类型
   * @param  integer    $group_id  用户所在组ID
   * @return array
   */
  public function changeUserType($id, $user_type, $group_id = 0){
    $res = ["status" => false, "msg" => "修改用户类型失败"];

    $data = ["id" => $id, "user_type" => $user_type];

    $GroupModel = new AuthGroup();
    if($user_type == "manager"){
      if($group_id < 1){
        $group = $GroupModel->where("group_type", "manager")->where("id", intval($group_id))->find();
        if(empty($group)){
          $res["msg"] = "管理员组不能为空";
          return $res;
        }
      }
      $data["manager_group_id"] = $group_id;
    }else if($user_type == "member"){
      $data["manager_group_id"] = 0;
    }else{
      $res["msg"] = "用户类型不正确";
      return $res;
    }


    $this->allowField(true)->update($data);

    // 无论普通用户（member）还是管理员（manager）都有一条user_type='member'的GroupAccess记录
    // 所以，修改用户类型时只需要删除或增加user_type='manager'的GroupAccess
    // $AccessModel = new GroupAccess();
    // if($user_type == "member"){
    //   $AccessModel->deleteUserAccess($id, "manager");
    // }else{
    //   $AccessModel->createOrUpdateUserAccess($id, $group_id, "manager");
    // }
    $res = ["status" => true, "msg" => "修改用户类型成功"];
    return $res;
  }

  /**
   * 排序方式
   * @Author   zhanghong
   * @DateTime 2018-03-04
   * @param    string     $order_type 排序类型
   * @return   string
   */
  private function orderStr($order_type = 'default'){
    switch ($order_type) {
      case 'user_id':
        $order_str = 'user.id DESC';
      default:
        $order_str = 'user.id DESC';
        break;
    }

    return $order_str;
  }

  /**
   * 把请求参数转化成查询条件
   * @Author   zhanghong
   * @DateTime 2018-03-04
   * @param    [type]     $param [description]
   * @return   [type]            [description]
   */
  private function mapSearchParam($param){
    $map = [];
    foreach ($param as $key => $value) {
      if(!is_array($value)){
        $value = trim($value);
      }
      switch ($key) {
      case 'keyword':
        if(empty($value)){
          continue;
        }else{
          $keyword = "%{$value}%";
        }
        $map['user.name'] = ['user.name|user.nickname|user.email|user.mobile', 'LIKE', "%{$value}%"];
        break;
      case 'user_status':
        $map['user.status'] = ['user.status', '=', boolval($value)];
        break;
      case 'user_type':
        $map['user.type'] = ['user.type', '=', $value];
        break;
      }
    }
    return $map;
  }

  /**
   * MD5加密密码
   * @Author zhanghong
   * @Date   2016-12-22
   * @param  string     $password 明文密码
   * @param  string     $authcode 加密key
   * @return string               加密后的密码
   */
  private function md5Password($password, $authcode=''){
    if(empty($authcode)){
      $authcode = Config::get("authcode");
    }
    $result = "###".md5(md5($authcode.$password));
    return $result;
  }
}